/**

 * 版权所有(C)，上海勾芒信息科技，2017，所有权利保留。
 *
 * 项目名：cards-web 
 * 文件名：:MutiChannelWeChatServiceImpl.java
 * 模块说明：	
 * 修改历史：
 * 2017年9月27日 - liyang - 创建。
 *
 */
package com.thor.palmmall.impl.pay;

import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.UnsupportedEncodingException;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.security.SecureRandom;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.TreeMap;

import org.apache.commons.codec.binary.Hex;
import org.apache.commons.codec.digest.DigestUtils;
import org.apache.http.entity.ContentType;
import org.apache.http.entity.InputStreamEntity;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;

import com.alibaba.fastjson.JSON;
import com.google.gson.Gson;
import com.thor.palmmall.api.pay.wechat.MutiChannelWeChatService;
import com.thor.palmmall.api.pay.wechat.entity.RsPaySignature;
import com.thor.palmmall.api.pay.wechat.entity.WeChatUnifiedOrderResponse;
import com.thor.palmmall.api.pay.wechat.entity.WechatPayNotify;
import com.thor.palmmall.api.pay.wechat.entity.WechatUnifiedOrder;
import com.thor.palmmall.api.statement.AStatementPayBill;
import com.thor.palmmall.api.statement.AStatementPayBillState;
import com.thor.palmmall.api.statement.StatementService;
import com.thor.palmmall.rest.event.StatementOrderSyncEvent;
import com.thor.palmmall.rest.user.AChannelContents;
import com.thor.palmmall.util.event.GlobalEventBus;
import com.thor.palmmall.util.pay.SignUtil;
import com.thor.palmmall.util.pay.WebUtils;
import com.thor.palmmall.util.pay.XmlUtil;

/**
 * @author:liyang
 * @time:2017年9月27日 下午5:55:26
 * @since:1.0
 */
public class MutiChannelWeChatServiceImpl implements MutiChannelWeChatService {
  private static Logger logger = LoggerFactory.getLogger(MutiChannelWeChatServiceImpl.class);
  private String mchKey;
  private static final String NOTIFY_FLAG_SUCCESS = "success";
  private static final String NOTIFY_FLAG_FAILED = "failed";

  @Override
  public RsPaySignature unifiedOrder(WechatUnifiedOrder unifiedOrder, AChannelContents channel) {
    // 历史原因，现在用appSecret字段放置商户api秘钥
    this.mchKey = channel.getAPPSECRET();

    unifiedOrder.setAppid(channel.getAPP_ID());
    unifiedOrder.setMch_id(channel.getMCHID());
    unifiedOrder.setTrade_type("APP");
    unifiedOrder.setNotify_url(channel.getNOTIFYPAY());
    unifiedOrder.setNonce_str(nonce_str(16));

    try {
      unifiedOrder.setSign(sign(SignUtil.bean2TreeMap(unifiedOrder)).toUpperCase());
      logger.info(new Gson().toJson(unifiedOrder));
      String tempXmlStr = XmlUtil.beanToXml(new ByteArrayOutputStream(), unifiedOrder);
      String requestXml = tempXmlStr != null ? tempXmlStr.substring(55) : "";
      logger.info("xml转义后内容:" + requestXml);
      InputStreamEntity inputStreamEntity = new InputStreamEntity(
          new ByteArrayInputStream(requestXml.getBytes("UTF-8")), ContentType.APPLICATION_XML);
      String resultXml = WebUtils.post(channel.getUNIFIEDORDERURL(), inputStreamEntity);
      logger.info("微信返回内容:" + resultXml);
      WeChatUnifiedOrderResponse resp = XmlUtil.xmlToBean(resultXml,
          WeChatUnifiedOrderResponse.class);
      if (resp != null) {
        resp.setTimeStamp(unifiedOrder.getTimeStamp());
      }

      RsPaySignature sign = new RsPaySignature();
      sign.setAppId(channel.getAPP_ID());
      sign.setPackageStr("Sign=WXPay");
      sign.setPartnerId(channel.getMCHID());
      sign.setPrepayId(resp.getPrepayId());
      sign.setSign(SignUtil.createSign(sign, this.mchKey));
      return sign;
    } catch (UnsupportedEncodingException e) {
      e.printStackTrace();
    }
    return null;
  }

  @Override
  public boolean verifyNotify(String request) {
    try {
      WechatPayNotify notice = XmlUtil.xmlToBean(request, WechatPayNotify.class);
      if (notice == null) {
        return false;
      }
      logger.debug("微信回调参数:" + JSON.toJSONString(notice));
      String sign = SignUtil.createSign(notice, this.mchKey);
      boolean ischeck = sign.equals(notice.getSign());
      logger.debug("微信验签结果:" + ischeck);

      SimpleDateFormat fmt = new SimpleDateFormat("yyyyMMddHHmmss");
      AStatementPayBill bill = statementService.getByBillNumber(notice.getOut_trade_no());

      if (bill == null) {
        return false;
      }
      if (AStatementPayBillState.FINISH.equals(bill.getState())) {
        return true;
      }

      if (ischeck) {
        bill.setActionTime(fmt.parse(notice.getTime_end()));
        bill.setNotifyFlag(NOTIFY_FLAG_SUCCESS); // 回调使用单独的标志字段
        bill.setState(AStatementPayBillState.PAID);
        bill.setTransactioNo(notice.getTransaction_id());
        statementService.saveStatementPayBill(bill, bill.getCreateInfo());
        // 开始同步订单任务
        StatementOrderSyncEvent event = new StatementOrderSyncEvent();
        event.setBill(bill);
        GlobalEventBus.post(event);
        return true;
      } else {
        logger.info("验证签名失败:" + notice.getReturn_msg());
        bill.setErrMsg(notice.getReturn_msg());
        bill.setNotifyFlag(NOTIFY_FLAG_FAILED);
        statementService.saveStatementPayBill(bill, bill.getCreateInfo());
      }
    } catch (Exception e) {
      e.printStackTrace();
    }
    return false;
  }

  /**
   * 微信nonce_str生成算法
   * 
   * @param bits
   *          生成位数,选择64bit
   * @return 生成后的nonce_str
   */
  public String nonce_str(int bits) {
    final byte[] bytes;
    try {
      SecureRandom secureRandom = SecureRandom.getInstance("SHA1PRNG");
      if ((bits % 8) != 0) {
        throw new IllegalArgumentException("Size is not divisible by 8!");
      }
      bytes = new byte[bits / 8];
      secureRandom.nextBytes(bytes);
      return Hex.encodeHexString(bytes);
    } catch (NoSuchAlgorithmException e) {
      e.printStackTrace();
    }
    return System.currentTimeMillis() + "";
  }

  /**
   * 对请求进行签名
   * 
   * @param param
   *          要签名的参数
   * @return
   * @throws UnsupportedEncodingException
   */
  public String sign(TreeMap<String, ?> param) throws UnsupportedEncodingException {
    String paramUrl = SignUtil.joinKeyValue(new TreeMap<String, Object>(param), null,
        "&key=" + this.mchKey, "&", true, "sign_type", "sign");
    logger.info("微信待签名串:" + paramUrl);
    MessageDigest digestUtils = DigestUtils.getMd5Digest();
    digestUtils.update(paramUrl.getBytes("UTF-8"));
    byte[] sign = digestUtils.digest();
    String result = Hex.encodeHexString(sign);
    logger.info("签名结果:" + result);
    return result;
  }

  @Autowired
  private StatementService statementService;

}
